const Runner = require('./Runner.js');
const Navigation = require('./Navigation.js');
const Actions = require('./Actions.js');

const {
    createPackage
} = require('./Package.js');

let flowCount = 1;

function init(option = {}) {
    this.$name = option.name;
    this.$path = option.path;
    this.$configs = option.configs || {};
    this.$runner = new Runner(this);
    this.$navigator = new Navigation(this);
    this.$actions = new Actions(this);
    this._option = option;
    this._onBeforePlugins = new Set();
    this._onAfterPlugins = new Set();
    this._onStartPlugins = new Set();
    this._onEndPlugins = new Set();
    this._onErrorPlugins = new Set();
}

class Flow {
    constructor(setting) {
        this._id = flowCount++;
        this.$setting = setting;
        if (setting.data) {
            this.$data = typeof setting.data == 'function' ? setting.data() : (setting.data || {});
        }
    }
    $init(option) {
        init.call(this, option);
        let {
            methods,
            onBeforeEach,
            onAfterEach,
            onEndEach,
            onStartEach,
            onError
        } = option;
        this.$actions.$set(methods);
        if (typeof onBeforeEach === 'function') {
            this._onBeforePlugins.add(onBeforeEach)
        }
        if (typeof onAfterEach === 'function') {
            this._onAfterPlugins.add(onAfterEach)
        }
        if (typeof onStartEach === 'function') {
            this._onStartPlugins.add(onStartEach)
        }
        if (typeof onEndEach === 'function') {
            this._onEndPlugins.add(onEndEach)
        }
        if (typeof onError === 'function') {
            this._onErrorPlugins.add(onError)
        }
    }
    $start(...args) {
        return this.$navigator.$next('Start').map(target => {
            return this.$run(target, ...args);
        })[0];
    }
    async $run(target, ...args) {
        let pack = createPackage({
            target,
            args,
            root: this
        });
        this.$runner.$startEach(pack);
        this.$runner.$run(pack);
        return pack;
    }
    $destroy() {
        this.$status = 'destroyed';
        this.$name = null;
        this.$path = null;
        this.$configs = null;
        this.$setting = null;
        this.$runner = null;
        this.$navigator = null;
        this.$actions = null;
        this._option = null;
        this._onBeforePlugins = null;
        this._onAfterPlugins = null;
        this._onStartPlugins = null;
        this._onEndPlugins = null;
        this._onErrorPlugins = null;
        this._id = null;
    }
    $methods(id, cb) {
        if (!id) {
            return
        }
        if (typeof id == 'object') {
            Object.keys(id).forEach(key => {
                this.$methods(key, id[key]);
            })
            return;
        }
        if (typeof cb === 'function') {
            this.$actions.$set({
                [id]: (...arg) => {
                    cb(...arg)
                    return arg[0]
                }
            });
        }
        return this;
    }
    $on(evt , callback) {
       switch(evt){
            case 'startEach':
                this._onStartPlugins.add(callback);
                break;
            case 'beforeEach':
                this._onBeforePlugins.add(callback);
                break;
            case 'afterEach':
                this._onAfterPlugins.add(callback);
                break;
            case 'endEach':
                this._onEndPlugins.add(callback);
                break;
            case 'errorEach':
                this._onErrorPlugins.add(callback);
                break;
       }
    }
    $off(evt , callback) {
        switch(evt){
            case 'startEach':
                this._onStartPlugins.delete(callback);
                break;
            case 'beforeEach':
                this._onBeforePlugins.delete(callback);
                break;
            case 'afterEach':
                this._onAfterPlugins.delete(callback);
                break;
            case 'endEach':
                this._onEndPlugins.delete(callback);
                break;
            case 'errorEach':
                this._onErrorPlugins.delete(callback);
                break;
        }
    }
}

module.exports = Flow;
